/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
 */

#include "log.hpp"
#include "Buffer.h"
#include "Utils.h"

namespace OHTestApp {
namespace {
// 静态标志，用于控制高频操作的日志输出
static bool s_loggedGetBufferMemoryRequirements = false;
static bool s_loggedBindBufferMemory = false;
static bool s_loggedMapMemory = false;
static bool s_loggedUnmapMemory = false;
static bool s_loggedFlushMappedMemoryRanges = false;
} // end of anonymous namespace

bool Buffer::Init(const BufferDescriptor &descriptor)
{
    VkDevice device = Device::Instance().GetNativeDevice();
    VkBufferCreateInfo bufferInfo{};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = descriptor.bufferSize;
    bufferInfo.usage = descriptor.usage;
    bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

    VkResult result = vkCreateBuffer(device, &bufferInfo, nullptr, &m_buffer);
    if (result == VK_SUCCESS) {
        GOLOGW("vkCreateBuffer completed successfully for buffer %s", descriptor.name.c_str());
    } else {
        GOLOGE("vkCreateBuffer failed with error: %d for buffer %s", result, descriptor.name.c_str());
        return false;
    }

    VkMemoryRequirements memRequirements;
    vkGetBufferMemoryRequirements(device, m_buffer, &memRequirements);
    if (!s_loggedGetBufferMemoryRequirements) {
        GOLOGW("vkGetBufferMemoryRequirements completed successfully");
        s_loggedGetBufferMemoryRequirements = true;
    }

    VkMemoryAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex =
        Device::Instance().FindMemoryType(memRequirements.memoryTypeBits, descriptor.properties);

    result = vkAllocateMemory(device, &allocInfo, nullptr, &m_bufferMemory);
    if (result == VK_SUCCESS) {
        GOLOGW("vkAllocateMemory completed successfully for buffer %s", descriptor.name.c_str());
    } else {
        GOLOGE("vkAllocateMemory failed with error: %d for buffer %s", result, descriptor.name.c_str());
        return false;
    }

    result = vkBindBufferMemory(device, m_buffer, m_bufferMemory, 0);
    if (result == VK_SUCCESS) {
        if (!s_loggedBindBufferMemory) {
            GOLOGW("vkBindBufferMemory completed successfully");
            s_loggedBindBufferMemory = true;
        }
    } else {
        GOLOGE("vkBindBufferMemory failed with error: %d for buffer %s", result, descriptor.name.c_str());
        return false;
    }

    m_descriptor = descriptor;
    return true;
}

void *Buffer::Map(size_t size, size_t offset)
{
    if (m_mappedAddr) {
        return m_mappedAddr;
    }
    
    VkResult result = vkMapMemory(Device::Instance().GetNativeDevice(), m_bufferMemory, 0, size, 0, &m_mappedAddr);
    if (result == VK_SUCCESS) {
        if (!s_loggedMapMemory) {
            GOLOGW("vkMapMemory completed successfully");
            s_loggedMapMemory = true;
        }
    } else {
        GOLOGE("vkMapMemory failed with error: %d for buffer %s", result, m_descriptor.name.c_str());
        m_mappedAddr = nullptr;
    }
    
    return m_mappedAddr;
}

void Buffer::Unmap(void *data)
{
    vkUnmapMemory(Device::Instance().GetNativeDevice(), m_bufferMemory);
    if (!s_loggedUnmapMemory) {
        GOLOGW("vkUnmapMemory completed successfully");
        s_loggedUnmapMemory = true;
    }
}

void Buffer::Destroy()
{
    if (m_mappedAddr) {
        Unmap(m_mappedAddr);
        m_mappedAddr = nullptr;
    }
    if (m_buffer != VK_NULL_HANDLE) {
        GOLOGD("Destroying buffer %s", m_descriptor.name.c_str());
        vkDestroyBuffer(Device::Instance().GetNativeDevice(), m_buffer, nullptr);
        GOLOGW("vkDestroyBuffer completed successfully");
        m_buffer = VK_NULL_HANDLE;
    }
    if (m_bufferMemory != VK_NULL_HANDLE) {
        GOLOGD("Freeing buffer memory %s", m_descriptor.name.c_str());
        vkFreeMemory(Device::Instance().GetNativeDevice(), m_bufferMemory, nullptr);
        GOLOGW("vkFreeMemory completed successfully");
        m_bufferMemory = VK_NULL_HANDLE;
    }
}

void Buffer::UpdateData(void *data, size_t size)
{
    if (!data) {
        GOLOGE("Buffer::UpdateData: data pointer is null for buffer %s", m_descriptor.name.c_str());
        return;
    }
    
    if (size == 0) {
        GOLOGE("Buffer::UpdateData: size is zero for buffer %s", m_descriptor.name.c_str());
        return;
    }
    
    if (size > m_descriptor.bufferSize) {
        GOLOGE("Data size overflow. Buffer size %zu less than data size %zu needed to be updated!",
            m_descriptor.bufferSize, size);
        return;
    }

    void *mappedAddr = Map(size, 0);
    if (!mappedAddr) {
        GOLOGE("Map buffer failed when updating buffer %s data.", m_descriptor.name.c_str());
        return;
    }

   // 使用 memcpy 进行数据复制
    std::memcpy(mappedAddr, data, size);
    
    // 由于移除了 HOST_COHERENT，需要手动 flush 到设备
    VkMappedMemoryRange flushRange{
        .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
        .pNext = nullptr,
        .memory = m_bufferMemory,
        .offset = 0,
        .size = size
    };
    
    VkResult result = vkFlushMappedMemoryRanges(Device::Instance().GetNativeDevice(), 1, &flushRange);
    if (result != VK_SUCCESS) {
        GOLOGE("vkFlushMappedMemoryRanges failed with error: %d for buffer %s", result, m_descriptor.name.c_str());
    } else {
        if (!s_loggedFlushMappedMemoryRanges) {
            GOLOGW("vkFlushMappedMemoryRanges completed successfully");
            s_loggedFlushMappedMemoryRanges = true;
            GOLOGI("Buffer::UpdateData successfully flushed %zu bytes to device for buffer %s", 
               size, m_descriptor.name.c_str());
        }
    }
}

VkBuffer Buffer::GetNativeBuffer()
{
    return m_buffer;
}

Buffer Buffer::CreateUniform(size_t size)
{
    Buffer buffer;
    BufferDescriptor bufferDesc;
    bufferDesc.name = "UniformBuffer";
    bufferDesc.bufferSize = size;
    bufferDesc.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
    // bufferDesc.properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
    bufferDesc.properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
    buffer.Init(bufferDesc);
    return buffer;
}

size_t Buffer::GetSize()
{
    return m_descriptor.bufferSize;
}
}
// namespace OHTestApp